/*
 * Copyright 2009, 2010, YUNNAN Co-soft Information Technology Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cs.website.client.captcha;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import javax.imageio.ImageIO;

/**
 * CaptchaGenerator generator for <a href="http://code.google.com/p/co-soft">
 * CaptchaGenerator</a> configurations.
 *
 * @author <a href="mailto:toughPatient5@gmail.com">Gang Liu</a>
 * @version 1.0.0.0, Apr 29, 2010
 */
public class CaptchaGenerator {

    public static final int NUM_OF_CHAR = 4;
    public static final int WIDTH_PER_CHAR = 20;
    public static final int HEIGHT_PER_CHAR = 25;
    public static final float CHAR_MAX_SIZE = 30.0f;
    public static final String IMAGE_FILE_NAME = "code";
    public static final String IMAGE_FILE_EXT = "png";
    public static final char[] LOWER_CASES = {'a', 'b', 'c', 'd', 'e', 'f', 'g',
        'h', 'i', 'j', 'k', 'l', 'm', 'n',
        'o', 'p', 'q', 'r', 's', 't', 'u',
        'v', 'w', 'x', 'y', 'z'};
    public static final char[] UPPER_CASE = {'A', 'B', 'C', 'D', 'E', 'F', 'G',
        'H', 'I', 'J', 'K', 'L', 'M', 'N',
        'O', 'P', 'Q', 'R', 'S', 'T', 'U',
        'V', 'W', 'X', 'Y', 'Z'};
    public static final char[] DIGIT = {'0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9'};
    public static final String[] FONTS_USED = {"Arial", "Helvetica", "Serif",
        "Times"};
    public static final Font[] SYSTEM_FONTS;

    static {
        final Set<Font> temp = new HashSet<Font>();
        for (final Font font : GraphicsEnvironment.getLocalGraphicsEnvironment().
                getAllFonts()) {
            if (eligibleFont(font)) {
                temp.add(font);
            }
        }
        SYSTEM_FONTS = temp.toArray(new Font[0]);
    }

    public static CaptchaUnit getCattcha() throws IOException {
        final String text = getRandomString();
        final BufferedImage img = getImageFile(text);
        return new CaptchaUnit(img, text);
    }

    private static String getRandomString() {
        final Random rand = new Random();
        //Generates digit
        final StringBuilder sb = new StringBuilder();
        for (int index = 0; index < NUM_OF_CHAR; index++) {
            final char[] target;
            final boolean isDigit = rand.nextBoolean();
            final boolean isUpperCase = rand.nextBoolean();

            //Random to determine if generate digit, lowercase or upper case char
            if (isDigit) {
                target = DIGIT;
            } else if (isUpperCase) {
                target = UPPER_CASE;
            } else {
                target = LOWER_CASES;
            }

            sb.append(target[rand.nextInt(target.length)]);
        }
        return sb.toString();
    }

    private static BufferedImage getImageFile(final String input) throws
            IOException {
        final BufferedImage img = new BufferedImage(NUM_OF_CHAR * WIDTH_PER_CHAR,
                HEIGHT_PER_CHAR,
                BufferedImage.TYPE_INT_RGB);
        makeNoise(img);
        final Graphics g = img.getGraphics();
        final Random rand = new Random();

        g.setColor(new Color(75, 75, 75));
        final StringBuilder debugSb = new StringBuilder();
        for (int charIndex = 0; charIndex < input.length(); charIndex++) {
            final char[] chars = input.toCharArray();
            //Generates  style of BOLD(1), ITALIC(2), or PLAIN(0) randomly
            final int style = rand.nextInt(2);
            final float size = ((95.0f - rand.nextInt(30)) / 100)
                    * CHAR_MAX_SIZE;
            final int xPosition = rand.nextInt(5);
            final Font randFont = SYSTEM_FONTS[rand.nextInt(
                    SYSTEM_FONTS.length)].deriveFont(style, size);
            g.setFont(randFont);
            debugSb.append(randFont.getFontName() + " : ");
            g.drawChars(chars, charIndex, 1, charIndex * WIDTH_PER_CHAR
                    + xPosition, 22);
        }

        return img;
    }

    private static void makeNoise(final BufferedImage img) {
        final WritableRaster raster = img.getRaster();
        final Random rand = new Random();
        for (int width = 0; width < raster.getWidth(); width++) {
            for (int height = 0; height < raster.getHeight(); height++) {
                final int[] pix = new int[3];
                if (rand.nextFloat() > 0.8) {
                    pix[0] = 100;
                    pix[1] = 100;
                    pix[2] = 100;
                } else {
                    pix[0] = 250;
                    pix[1] = 250;
                    pix[2] = 250;
                }
                raster.setPixel(width, height, pix);
            }
        }
    }

    private static boolean eligibleFont(final Font font) {
        final String fontName = font.getName();
        for (final String fontsUsed : FONTS_USED) {
            if (fontName.toUpperCase().contains(fontsUsed.toUpperCase())) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) throws IOException {
        final String input = CaptchaGenerator.getRandomString();
        final File f = new File("c:\\test.png");
        ImageIO.write((RenderedImage) CaptchaGenerator.getImageFile(input),
                "png", f);
        System.out.println(f.getName() + " " + input);
    }
}
